iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
AI Engineering

30 天打造 AI 後端:從 LLM、RAG 到 AI Agent系列 第 15

[DAY 15]RAG 到底要評估什麼?我發現 Retrieval 命中率根本不等於答案正確率

  • 分享至 

  • xImage
  •  

同一組 15 題、同一份語料、同一次檢索,我算了兩個分數:

  • 檢索命中率(top-1,排第一名的是不是我要的那條):10/15,怎麼調都不動
  • 答案正確率:2 → 10 → 14 → 15/15

我前幾天都在想辦法把 top-1 從 9 推到 10。今天並排一看才發現,我盯的那個分數
天花板比實際表現還低 5 題

(語料:虛構的《員工工作規則》59 條。檢索退回 Day 11 的 Chroma、參數凍結,
生成用 gpt-4o-minitemperature=0。)

一、top-1 為什麼會騙人

它假設看檢索結果的是人。人只想看第一名,所以第一名要對。

但 RAG 的讀者是 LLM,它會把餵進去的 k 段全部讀完,順序幾乎不重要。
假設換了,尺就得換。

組別 top-1 recall@k 答案正確 tokens(輸入/輸出)
完全不給資料 2/15 952/856
k=1 10/15 10/15 10/15 3649/554
k=3 10/15 14/15 14/15 7952/609
k=5 10/15 14/15 15/15 12403/613

中間兩欄走勢完全一致,而最左邊那欄從頭到尾沒動。

先講清楚 recall@k 是什麼

檢索做的事,是把 59 條規章依「跟問題像不像」排名。k 就是我決定拿前幾名
餵給模型。
以第 7 題「特休沒休完會怎樣?」為例,答案寫在第 23 條,
而實際排名是:

第1名  第 22 條(特休假)
第2名  第 26 條(補休)
第3名  第 23 條  ← 答案在這

k=1 只餵第 1 名,模型只看到第 22 條,於是回「規章條文中未提及」;
k=3 餵前三名,第 23 條進去了,它就答得出「遞延至次年三月三十一日」。
同一次檢索、同一題,只因為 k 不一樣,結果就不一樣。

所以兩把尺的差別只有一句話:top-1 問「答案有沒有排第 1 名」,
recall@k 問「答案有沒有擠進前 k 名」
,第 2、第 3 名都算。

15 題裡有 10 題的答案排第 1 名(top-1 = 10/15),另外 4 題排第 2、3 名。
k=1 時這 4 題全漏掉,k=3 一次全撿回來(recall@3 = 14/15)。
top-1 不管 k 設多少都不會變,它只看第一名——這就是為什麼我調了幾天
它都是 10/15。

二、要評估的其實有四層

每層都要配一句「壞掉時會長什麼樣」——出事的時候你需要的不是分數,
是知道要改哪裡。

指標 今天的數字 壞掉時的症狀
檢索層 recall@k 14/15(k=3) 答案寫「規章條文中未提及」
生成層 答案正確率 14/15(k=3) 條文在 prompt 裡,數字卻抄錯
歸因層 檢索 × 答案交叉表 「撈到卻答錯」= 0 不知道下一塊錢該花在切塊還是 prompt
成本層 輸入/輸出 token k=3 只花 k=5 的 64% 分數升 1 題、帳單升 3 倍而你沒發現

top-1 不在這張表上。 它是檢索層偏低的代理指標:k=3 時它 10/15、
recall@k 14/15,差 4 題。我優化的是低的那一欄。

三、交叉表才知道要改哪裡

每題記兩件事:檢索有沒有撈到、答案對不對。

組別 撈到且答對 撈到但答錯 沒撈到卻答對 沒撈到且答錯
k=1 10 0 0 5
k=3 14 0 0 1
k=5 14 0 1 0

「撈到但答錯」三組都是 0,出乎我意料。只要條文進了 prompt,模型就照抄數字。
而且預期條文排到第 3 名,答對率一樣是 100%。

兩個能直接用的結論:追 recall@k,別追 top-1
先別急著上 reranker,重排序解的是「排太後面讀不到」,
這問題不存在,k 從 1 拉到 3 就多對 4 題。

但這 15 題都是單一條文可答

所以我另外補了一題要湊兩條才回答得出來的:「我平日加班 3 小時,加班費要
怎麼算?」——第 19 條給加成比例,第 33 條才寫明計算基準是本薪。

組別 第 19 條 第 33 條 兩條都到 答案正確
k=1 第 1 名 未進
k=3 第 1 名 未進
k=5 第 1 名 未進
k=10 第 1 名 第 9 名

第 19 條穩坐第一名,第 33 條要 k=10 才擠得進來。
單條 recall 很好看,兩條的聯集很難看。 所以 recall@k 要看的是
「需要的條文有沒有全部到齊」,不是「有沒有到一條」。

但 k=3 就判 ✅ 了。因為第 19 條自己寫著「加班費之計算基準為第 33 條所定之
本薪加計經常性給與」——模型抄到「本薪」兩個字,卻從來沒讀過本薪的定義。
又是一題猜對的(下一節還有一題)。

順帶一提,不給條文的時候它答「基本時薪的 1.5 倍」,我的規章裡沒有這條。

四、最容易被省略的一組:完全不給資料

不檢索、不給條文,直接問同樣 15 題,只對 2 題——而那兩題剛好是我的規章跟
勞基法一致的。它答對的不是我的規章。

錯的 13 題不是回答不知道,是掛著「根據公司規章」講錯數字:婚假答五天
(實際八日)、事假答七天(實際十四日)、特休沒休完答「自動作廢」
(實際遞延到次年 3/31)。

從 2/15 到 15/15,中間那 13 題全是檢索換來的。 沒有這組對照,
你分不出分數是資料的功勞,還是模型本來就會。

順帶一提,這組輸出 856 tokens 比 k=5 的 613 還多。沒資料可講的時候它話最多。

五、另一個 15/15 也是假的

判定我用關鍵事實比對:每題先寫下答案必須出現的事實,全中才算對。
不用 LLM 當裁判,因為裁判會偏心、重跑要再付錢、數字不可重現。

{"id": 1, "question": "病假連續請幾天以上需要附診斷證明?",
 "expected": "第 24 條", "facts": ["三日", "診斷證明"], "forbid": []}

然後第 9 題「沒來上班也沒跟任何人講,會有什麼後果?」把它玩壞了。
預期第 32 條,facts 只寫了「曠職」一個詞。k=5 撈回第 47、12、49、13、16 條,
第 32 條根本沒進 prompt,模型卻答「將視為曠職,依第八章獎懲規定處理」,
判定 ✅。

但「曠職」在整段 context 只出現一次,在第 16 條(天然災害):
「……公司不得視為曠職。」意思是相反的。模型從「不得視為曠職」生出
「將視為曠職」,結論剛好對。

總表最誠實的寫法是「k=5:14/15,外加 1 題猜中」。

同一題在 k=1/k=3 還藏了另一種失敗:它答「根據第 47 條……」,
第 47 條真的在 prompt 裡而且排第一名,但那條講的是洩漏公司資料的懲處。
引用查得到,內容答非所問——這比憑空掰條號更難抓,因為使用者去查會查到,
而「有附引用」本身就會讓他更相信答案。

所以:分數再高都要抽樣人工複核,而且優先複核答對的題
答錯的你自然會去看,答對的才是偏誤的藏身處。

小結

檢索的成績單和 RAG 的成績單不是同一張。真正決定答案對不對的是 recall@k,
而我一直看的是低的那一欄。

這次成本:主實驗 60 次生成約新台幣 0.165 元,多條文那題 5 次約 0.025 元。
一份能反覆重跑、逐題留紀錄的評估,成本是兩毛錢
判定邏輯改幾版都不用重新付錢生成。

還有一件更難受的:今天生成端一題都沒搞砸,代表這 15 題根本不難。
全對的實驗代表考題太簡單。

你的 RAG 上線前,量的是檢索命中率,還是答案正確率?

明天 Day 16:加五題語料裡根本沒有答案的陷阱題,量幻覺率、該拒答時有沒有
拒答、不該拒答時是不是亂拒答,以及引用的條號對不對。

GitHub:https://github.com/wp900622/TrustRAGday15_rag/


上一篇
[Day 14] 我前六天在優化錯的東西:top-1 命中 10/15,答案卻對了 15/15
下一篇
[Day 16] 最危險的答案是「規章沒寫,但國內是三千元」
系列文
30 天打造 AI 後端:從 LLM、RAG 到 AI Agent26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言